package esim.service.service.impl;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import esim.service.domain.Bills;
import esim.service.domain.Invoice;
import esim.service.domain.Order;
import esim.service.entity.*;
import esim.service.enums.BillsStatus;
import esim.service.enums.InvoiceStatus;
import esim.service.enums.OrderStatus;
import esim.service.mapper.InvoiceMapper;
import esim.service.mapper.OrderMapper;
import esim.service.service.BillsService;
import esim.service.service.InvoiceService;
import esim.service.supports.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.function.Consumer;

/**
 * @author wbb
 */
@Service
@Slf4j
public class InvoiceServiceImpl implements InvoiceService {
    //最大发送邮箱次数
    private static final Integer MAXSENDCOUNT = 5;
    private final ShdzfpTransId transId;
    private final InvoiceMapper invoiceMapper;
    private final OrderMapper orderMapper;
    private final ShdzfpProperty property;
    private final BillsService billsService;

    private ExecutorService executorService = Executors.newFixedThreadPool(4);

    private final int DELAY_MINUTE = 1;

    @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
    public InvoiceServiceImpl(InvoiceMapper invoiceMapper, OrderMapper orderMapper, InstanceProperty instanceProperty, BillsService billsService) {
        this.invoiceMapper = invoiceMapper;
        this.orderMapper = orderMapper;
        this.property = instanceProperty.getShdzfp();
        this.transId = new ShdzfpTransId(property.getUserName());
        this.billsService = billsService;
    }

    @Override
    public String insert(Invoice modal) {
        throw new RuntimeException("该功能暂不支持");
    }

    @Override
    public Invoice queryById(Integer id) {
        return invoiceMapper.selectByPrimaryKey(id);
    }

    @Override
    public PageInfo<Invoice> list(Query<Invoice> query) {
        PageHelper.offsetPage(query.getOffset(), query.getLimit());
        PageHelper.orderBy(query.getOrderBy());
        List<Invoice> list = invoiceMapper.selectAll();
        return new PageInfo<>(list);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean create(InvoiceInfo info, Integer userId) {
        List<Integer> orderIds = info.getOrderIds();
        List<Order> list = new ArrayList<>(orderIds.size());
        Invoice invoice = createRow(info);
        invoice.setUserId(userId);
        invoice.setStatus(InvoiceStatus.K_I);
        invoiceMapper.insert(invoice);
        for (Integer orderId : orderIds) {
            Order order = orderMapper.selectByPrimaryKey(orderId);
            checkOrderStatus(order, userId);
            list.add(order);
            orderMapper.setInvoiceIdById(orderId, invoice.getId());
        }

        BigDecimal orderTotalAmount = list.stream().map(Order::getOrderAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
        invoice.setAmount(orderTotalAmount);
        invoiceMapper.updateByPrimaryKeySelective(invoice);

        int result = ShdzfpUtils.issueInvoice(list, info, property, invoice.getInvoiceNo());
        if (result == 0) {
            Timer timer = new Timer();
            Calendar calendar = Calendar.getInstance();
            //发送邮件延迟1分钟
            calendar.add(Calendar.MINUTE, this.DELAY_MINUTE);
            timer.schedule(new TimerTask() {
                @Override
                public void run() {
                    log.info("自动发送发票到邮箱: {}", invoice.getEmail());

                    try{
                        resend(invoice.getId(), invoice.getEmail());
                    }catch (Exception exception){
                        exception.printStackTrace();
                    }

                    timer.cancel();
                    timer.purge();
                }
            }, calendar.getTime());

            //更新订单所关联的账单开票状态
            updateOrderBills(list, invoice.getId(), InvoiceStatus.K_I);
        } else if(result == 1) {
            invoice.setStatus(InvoiceStatus.K_I);
            invoiceMapper.updateByPrimaryKeySelective(invoice);
            updateOrderBills(list, invoice.getId(), InvoiceStatus.K_I);
        }
        return true;
    }

    private void updateOrderBills(List<Order> orders, Integer invoiceId,int status){
        for (Order order : orders) {
            Bills bills = new Bills();
            bills.setOrderNo(order.getOrderNo());
            List<Bills> query = billsService.query(bills);
            if(!CollectionUtils.isEmpty(query)){
                Bills updateBills = query.get(0);
                updateBills.setInvoiceId(invoiceId);
                updateBills.setBillsStatus(BillsStatus.RECEIVED);
                updateBills.setInvoiceStatus(status);
                billsService.update(updateBills);
            }
        }
    }

    @Override
    public String loadPdfUrl(Integer invoiceId, Integer userId) {
        Invoice invoice = invoiceMapper.selectByPrimaryKey(invoiceId);
        if (invoice == null || !userId.equals(invoice.getUserId())) {
            throw new RuntimeException("发票不存在");
        }
        if (StringUtils.isEmpty(invoice.getPdfUrl())) {
            String nextId = transId.nextId();
            String url = ShdzfpUtils.downloadInvoice(property, nextId, invoice.getInvoiceNo());
            if (!StringUtils.isEmpty(url)) {
                Invoice record = new Invoice();
                record.setId(invoice.getId());
                record.setPdfUrl(url);
                invoiceMapper.updateByPrimaryKeySelective(record);
                return url;
            } else {
                throw new RuntimeException("请等待发票生成完毕");
            }
        } else {
            return invoice.getPdfUrl();
        }
    }

    @Override
    public List<Invoice> historys(Integer userId) {
        Invoice invoice = new Invoice();
        invoice.setUserId(userId);
        List<Invoice> select = invoiceMapper.select(invoice);
        select.sort((o1, o2) -> o2.getId()- o1.getId());
        return select;
    }

    @Override
    public PageInfo<Invoice> query(Query<Invoice> query) {
        PageHelper.offsetPage(query.getOffset(), query.getLimit());
        PageHelper.orderBy(query.getOrderBy());
        List<Invoice> list = invoiceMapper.query(query.getSearch());
        return new PageInfo<>(list);
    }

    @Override
    public boolean resend(Integer invoiceId, String email) {
        Invoice invoice = invoiceMapper.selectByPrimaryKey(invoiceId);
        if(invoice==null){
            throw new EsimException("未找到发票", HttpStatus.BAD_REQUEST.value());
        }
        if(invoice.getSendCound()>=MAXSENDCOUNT){
            throw new EsimException("重发邮箱次数已达上限", HttpStatus.BAD_REQUEST.value());
        }
        if(invoice.getFpDm() == null || invoice.getFpHm() == null){
            this.updateInvoiceDmAndHm(invoice);
        }
        Invoice updateInvoice = new Invoice();
        updateInvoice.setId(invoiceId);
        updateInvoice.setEmail(email);
        updateInvoice.setSendCound(invoice.getSendCound()+1);
        invoiceMapper.updateByPrimaryKeySelective(updateInvoice);

        ShdzfpUtils.sendMail(property,invoice.getInvoiceNo(), email!=null?email:invoice.getEmail(), invoice.getFpDm(), invoice.getFpHm());
        return true;
    }

    private void updateInvoiceDmAndHm(Invoice invoice){
        if(StringUtils.isBlank(invoice.getFpDm()) || StringUtils.isBlank(invoice.getFpHm())){
            FPInfo.Data.RESPONSE_FPMXXZ response_fpmxxz = ShdzfpUtils.invoiceDetail(property, invoice.getInvoiceNo());
            if(response_fpmxxz != null){
                if(response_fpmxxz.getFP_DM()!=null && response_fpmxxz.getFP_HM()!=null){
                    invoice.setFpDm(response_fpmxxz.getFP_DM());
                    invoice.setFpHm(response_fpmxxz.getFP_HM());
                    invoice.setPdfUrl(response_fpmxxz.getPDF_URL());
                    invoice.setStatus(InvoiceStatus.Y_K);
                    invoiceMapper.updateByPrimaryKeySelective(invoice);
                }
            }
        }
    }

    public void checkOrderStatus(Order order, Integer userId) {
        if (order == null || !userId.equals(order.getUserId())) {
            throw new EsimException("部分订单不存在", 5000);
        }
        if (order.getInvoiceId() != null) {
            throw new EsimException("同一订单不可重复开具发票", 5001);
        }
        Integer status = order.getOrderStatus();
        if (status == OrderStatus.PAYING || status == OrderStatus.PAYED) {
            throw new EsimException("支付中或未到账订单不可开票", 5002);
        } else if (status == OrderStatus.CLOSED) {
            throw new EsimException("已取消的订单不可开票", 5003);
        }
    }

    private Invoice createRow(InvoiceInfo info) {
        Invoice invoice = new Invoice();
        invoice.setInvoiceNo(transId.nextId());
        Date date = new Date();
        invoice.setInvoiceDate(date);
        invoice.setCrtTime(date);
        invoice.setPhoneNo(info.getPhone());
        invoice.setEmail(info.getEmail());
        invoice.setStatus(InvoiceStatus.Y_K);
        invoice.setOrderIds(StringUtils.join(info.getOrderIds(), ","));
        invoice.setGhfMc(info.getTitle());
        invoice.setGhfNsrsbh(info.getTaxpayerNumber());
        invoice.setGhfDz(info.getGhfDZ());
        invoice.setGhfDh(info.getGhfGDDH());
        invoice.setPhoneNo(info.getGhfGDDH());
        invoice.setGhfYh(info.getGhfYH());
        invoice.setGhfZh(info.getGhfZH());
        invoice.setSendCound(0);
        return invoice;
    }

    @Override
    public InvoiceDetail invoceDetail(Integer invoiceId) {
        Invoice invoice = invoiceMapper.selectByPrimaryKey(invoiceId);
        List<Order> orderList = new ArrayList<>();
        String orderIds = invoice.getOrderIds();
        if (!StringUtils.isEmpty(orderIds)) {
            String[] split = orderIds.split(",");
            for (String orderId : split) {
                Order order = orderMapper.selectByPrimaryKey(orderId);
                if (order != null) {
                    orderList.add(order);
                }
            }
        }

        orderList.sort(Comparator.comparingInt(Order::getId));

        InvoiceDetail invoiceDetail = new InvoiceDetail();
        invoiceDetail.setOrders(orderList);

        invoiceDetail.setInvoice(invoice);

        //FPInfo.Data.RESPONSE_FPMXXZ response_fpmxxz = ShdzfpUtils.invoiceDetail(property, invoice.getInvoiceNo());
        //invoiceDetail.setResponseFpmxxz(response_fpmxxz);

        return invoiceDetail;
    }

    @Override
    public int update(Invoice invoice) {
        return invoiceMapper.updateByPrimaryKeySelective(invoice);
    }

    @Override
    public List<Invoice> query(Invoice invoice) {
        return invoiceMapper.select(invoice);
    }

    @Override
    public Future<Integer> push(InvoceBody body) {
        return executorService.submit(() -> {
            Invoice condition = new Invoice();
            condition.setInvoiceNo(body.getResponse_fpmxxz().getFpqqlsh());
            invoiceMapper.query(condition).stream().findFirst().ifPresent(invoice -> {
                //更新发票状态
                if(body.getResponse_fpmxxz()!=null) {
                    invoice.setFpHm(body.getResponse_fpmxxz().getFp_hm());
                    invoice.setFpDm(body.getResponse_fpmxxz().getFp_dm());
                }
                invoice.setId(invoice.getId());
                invoice.setStatus(InvoiceStatus.Y_K);
                this.update(invoice);
                //发送发票到用户邮箱
                try{
                    this.resend(invoice.getId(),invoice.getEmail());
                }catch (Exception e){
                    log.error("",e);
                }
            });
            return 1;
        });
    }

    private static class SendInvoiceTask implements Runnable {
        private final DelayQueue<Message> queue;
        private final Consumer<Integer> consumer;
        public SendInvoiceTask(DelayQueue<Message> queue, Consumer<Integer> consumer) {
            this.queue = queue;
            this.consumer = consumer;
        }
        @Override
        public void run() {
            while (true) {
                try {
                    Message take = queue.take();
                    this.consumer.accept(take.getId());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
